/*
 * $Id: JsonConceptConnector.java 136 2009-07-16 07:53:43Z donbeave $
 */

package labour.concept.client.data.json;

import java.util.List;
import java.util.Map;

import labour.concept.client.ConceptConnector;
import labour.concept.client.data.DataTypeModel;
import labour.concept.client.data.DataTypeModelType;
import labour.concept.client.data.LocaleModel;
import labour.concept.client.data.LocaleModelType;
import labour.concept.client.data.ObjectModel;
import labour.concept.client.data.ObjectTextModel;
import labour.concept.client.data.ObjectTypeModel;
import labour.concept.client.data.ObjectTypeModelType;
import labour.concept.client.data.ObjectsLoadConfig;

import com.extjs.gxt.ui.client.data.DataReader;
import com.extjs.gxt.ui.client.data.HttpProxy;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.FormPanel;

public class JsonConceptConnector implements ConceptConnector {

  private String url;

  private JsonReadersContainer basicReader;
  private JsonLocalesReader<LocaleModel> localesReader;
  private JsonObjectsTypesReader<ObjectTypeModel> objectsTypesReader;
  private JsonDataTypesReader<DataTypeModel> dataTypesReader;
  private JsonObjectsReaderContainer objectsReader;

  public JsonConceptConnector(String url) {
    this.url = url;
  }

  public String getUrl() {
    return url;
  }


  public JsonReadersContainer getBasicReader() {
    if (basicReader == null) {
      basicReader = createBasicReader();
    }
    return basicReader;
  }

  public JsonLocalesReader<LocaleModel> getLocalesReader() {
    if (localesReader == null) {
      localesReader = createLocalesReader();
    }
    return localesReader;
  }

  public JsonObjectsTypesReader<ObjectTypeModel> getObjectsTypesReader() {
    if (objectsTypesReader == null) {
      objectsTypesReader = createObjectsTypesReader();
    }
    return objectsTypesReader;
  }

  public JsonDataTypesReader<DataTypeModel> getDataTypesReader() {
    if (dataTypesReader == null) {
      dataTypesReader = createDataTypesReader();
    }
    return dataTypesReader;
  }

  public JsonObjectsReaderContainer getObjectsReader() {
    if (objectsReader == null) {
      objectsReader = createObjectsReader();
    }
    return objectsReader;
  }

  @SuppressWarnings("unchecked")
  public void loadBasicData(AsyncCallback<Map<String, ?>> callback) {
    HttpProxy proxy = getBasicProxy();

    proxy.load(getBasicReader(), null, callback);
  }

  public void loadLocales(final AsyncCallback<List<LocaleModel>> callback) {
    loadBasicData(new AsyncCallback<Map<String, ?>>() {
      public void onFailure(Throwable caught) {
        callback.onFailure(caught);
      }

      @SuppressWarnings("unchecked")
      public void onSuccess(Map<String, ?> result) {
        callback.onSuccess((List<LocaleModel>) result.get("locales"));
      }
    });
  }

  public void loadObjectsTypes(final AsyncCallback<List<ObjectTypeModel>> callback) {
    loadBasicData(new AsyncCallback<Map<String, ?>>() {
      public void onFailure(Throwable caught) {
        callback.onFailure(caught);
      }

      @SuppressWarnings("unchecked")
      public void onSuccess(Map<String, ?> result) {
        callback.onSuccess((List<ObjectTypeModel>) result.get("objectsTypes"));
      }
    });
  }

  public void loadDataTypes(final AsyncCallback<List<DataTypeModel>> callback) {
    loadBasicData(new AsyncCallback<Map<String, ?>>() {
      public void onFailure(Throwable caught) {
        callback.onFailure(caught);
      }

      @SuppressWarnings("unchecked")
      public void onSuccess(Map<String, ?> result) {
        callback.onSuccess((List<DataTypeModel>) result.get("dataTypes"));
      }
    });
  }

  @SuppressWarnings("unchecked")
  public void saveObject(ObjectModel object, final List<ObjectTextModel> texts, AsyncCallback callback) {
    String path;

    // editinig object's data if it is exist
    if (object.isExist()) {
      path = getEditObjectPath(object);
    } else {
      if (object.getParentId() != null) {
        path = getAddObjectPath(object);
      } else {
        path = getAddObjectPath();
      }
    }

    RequestBuilder request = new RequestBuilder(RequestBuilder.POST, path);
    request.setHeader("Content-Type", FormPanel.ENCODING_URLENCODED);

    HttpProxy proxy = new HttpProxy(request) {
      @Override
      protected String generateUrl(Object loadConfig) {
        StringBuffer sb = new StringBuffer();

        ObjectModel model = (ObjectModel) loadConfig;

        sb.append("&item.TypeID=" + model.getTypeId());

        for (ObjectTextModel text : texts) {
          sb.append("&textItems." + text.getLocaleId() + ".Name=" + text.getName());
          sb.append("&textItems." + text.getLocaleId() + ".IsPublished=" + text.isPublished());
        }

        return sb.toString();
      }
    };

    proxy.load(null, object, callback);
  }

  @SuppressWarnings("unchecked")
  public void deleteObject(ObjectModel object, AsyncCallback callback) {
    RequestBuilder request = new RequestBuilder(RequestBuilder.POST, getDeleteObjectPath(object));
    request.setHeader("Content-Type", FormPanel.ENCODING_URLENCODED);

    HttpProxy proxy = new HttpProxy(request);

    proxy.load(null, null, callback);
  }

  @SuppressWarnings("unchecked")
  public void loadObjects(Object loadConfig, AsyncCallback callback) {
    String path = getLoadObjectsPath(loadConfig);
    DataReader reader = getObjectsReader();

    if (loadConfig instanceof ObjectsLoadConfig) {
      ObjectsLoadConfig config = (ObjectsLoadConfig) loadConfig;

      if (config.getUri() != null) {
        path = config.getUri();
      }
    }

    RequestBuilder request = new RequestBuilder(RequestBuilder.GET, path);

    HttpProxy proxy = new HttpProxy(request);

    proxy.load(reader, null, callback);
  }

  protected HttpProxy<Map<String, ?>> getBasicProxy() {
    RequestBuilder request = new RequestBuilder(RequestBuilder.GET, getInitPath());

    return new HttpProxy<Map<String, ?>>(request);
  }

  protected JsonReadersContainer createBasicReader() {
    JsonReadersContainer reader = new JsonReadersContainer("data");

    reader.registerReader("locales", getLocalesReader());
    reader.registerReader("objectsTypes", getObjectsTypesReader());
    reader.registerReader("dataTypes", getDataTypesReader());

    return reader;
  }

  protected JsonLocalesReader<LocaleModel> createLocalesReader() {
    return new JsonLocalesReader<LocaleModel>(new LocaleModelType());
  }

  protected JsonObjectsTypesReader<ObjectTypeModel> createObjectsTypesReader() {
    return new JsonObjectsTypesReader<ObjectTypeModel>(new ObjectTypeModelType());
  }

  protected JsonDataTypesReader<DataTypeModel> createDataTypesReader() {
    return new JsonDataTypesReader<DataTypeModel>(new DataTypeModelType());
  }

  protected JsonObjectsReaderContainer createObjectsReader() {
    return new JsonObjectsReaderContainer();
  }

  protected String getInitPath() {
    return getUrl() + "/init/";
  }

  protected String getLoadObjectsPath(Object loadConfig) {
    if (loadConfig instanceof String) {
      return getLoadObjectByIdPath((String) loadConfig);
    }
    else if (loadConfig instanceof ObjectModel) {
      return getLoadObjectChildrenPath((ObjectModel) loadConfig);
    }
    return getLoadRootObjectsPath();
  }

  protected String getLoadRootObjectsPath() {
    return getUrl() + "/objects/root/";
  }

  protected String getLoadObjectByIdPath(String id) {
    return getUrl() + "/objects/" + id + "/";
  }

  protected String getLoadObjectChildrenPath(ObjectModel parent) {
    return getUrl() + "/objects/" + parent.getId() + "/children/";
  }

  protected String getAddObjectPath() {
    return getUrl() + "/objects/add/";
  }

  protected String getAddObjectPath(ObjectModel model) {
    return getUrl() + "/objects/" + model.getParentId() + "/add/";
  }

  protected String getEditObjectPath(ObjectModel model) {
    return getUrl() + "/objects/" + model.getId() + "/update/";
  }

  protected String getDeleteObjectPath(ObjectModel model) {
    return getUrl() + "/objects/" + model.getId() + "/remove/";
  }

}
